package com.dries.netty.chat.netty;

import com.dries.netty.chat.session.SessionMap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;

import java.net.InetSocketAddress;

/**
 * @author Dries
 * @email 1451691457@qq.com
 */
@Slf4j
public class WebSocketServer {

    /**
     * 监听端口
     */
    @Value("${netty.port}")
    private Integer port;

    // 创建两个线程组, bossGroup处理连接，workerGroup处理数据
    private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    private final EventLoopGroup workerGroup = new NioEventLoopGroup(); // 默认8个NioEventLoopGroup
    private final ServerBootstrap bootstrap = new ServerBootstrap();

    /**
     * 单例
     */
    private static WebSocketServer singleInstance = new WebSocketServer();

    public static WebSocketServer getSingleInstance() {
        return singleInstance;
    }

    private Channel channel;

    /**
     * 启动即时通讯服务器
     */
    public void start() {
        final WebSocketServer webSocketServer = new WebSocketServer();

        ChannelFuture channelFuture = null;

        bootstrap.group(bossGroup, workerGroup)
                // 指定Channel
                .channel(NioServerSocketChannel.class)
                //使用指定的端口设置套接字地址
                // .localAddress(new InetSocketAddress(port))
                // 服务端可连接队列数
                .option(ChannelOption.SO_BACKLOG, 1024)
                // 设置TCP长连接（长时间无数据通信，TCP会自动发送一个活动探测数据报文）
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                //将小的数据包包装成更大的帧进行传送，提高网络的负载,即TCP延迟传输
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childHandler(new WSServerInitialzer());

        InetSocketAddress address = new InetSocketAddress(9999);
        channelFuture = bootstrap.bind(address);
        channelFuture.syncUninterruptibly();

        channel = channelFuture.channel();

        if (channelFuture.isSuccess()) {
            System.out.println("[----------------------------------------------------------]");
            System.out.println("【netty 服务器启动 端口号： " + address.getPort() + "】");
            System.out.println("[----------------------------------------------------------");
        }

        // 返回与当前Java应用程序关联的运行时对象
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                webSocketServer.stop();
            }
        });
        channelFuture.channel().closeFuture().syncUninterruptibly();

    }

    /**
     * 停止即时通讯服务器
     */
    public void stop() {
        if (channel != null) {
            channel.close();
        }

        SessionMap.getSingleInstance().shutdownGracefully();
        try {
            bossGroup.shutdownGracefully().sync();
            workerGroup.shutdownGracefully().sync();
            log.info("【netty 服务器关闭】");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}
